// Kony

#pragma once

#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "BaseAbilitySystemComponent.generated.h"

class ULoadScreenSaveGame;
//非动态 多播委托 效果资产标签，游戏标签容器
DECLARE_MULTICAST_DELEGATE_OneParam(FEffectAssetTags, const FGameplayTagContainer& /*AssetTags*/);
//能力发往覆盖组件控制器的多播委托
DECLARE_MULTICAST_DELEGATE(FAbilitesGiven)
//遍历所有已激活的游戏能力规格 多播委托
DECLARE_DELEGATE_OneParam(FForEachAbility,const FGameplayAbilitySpec&)
//能力状态变更多播委托，用于更新升级后激活的新技能,发送能力标签和状态标签 还有能力等级
DECLARE_MULTICAST_DELEGATE_FourParams(FAbilityStatusChanged, const FGameplayTag& /*AttributeTag*/, const FGameplayTag& /*InputTag*/, const FGameplayTag& /*StatusTag*/, int32 /*AbilityLevel*/)
//切换装备技能插槽 广播委托（这个非常重要,是指定或拖拽技能栏、未来道具栏也会类似做法）, 能力标签、状态标签、按键输入插槽标签、上一个输入插槽标签 （也就是比如从1拖拽到2 要清除这个标签内上一个技能的标签）
DECLARE_MULTICAST_DELEGATE_FourParams(FAbilityEquipped, const FGameplayTag& /*AbilityTag*/, const FGameplayTag& /*Status*/, const FGameplayTag& /*Slot*/, const FGameplayTag& /*PrevSlot*/)
//用于停止被动技能广播委托
DECLARE_MULTICAST_DELEGATE_OneParam(FDeactivatePassiveAbility, const FGameplayTag& /*AbilityTag*/)
//被动能力粒子组件激活效果广播委托
DECLARE_MULTICAST_DELEGATE_TwoParams(FActivatePassiveEffect, const FGameplayTag& /*AbilityTag*/, bool /*bActivate*/)

/**
 * 这是能力系统组件的基类
 */
UCLASS()
class GASC_HUANLING_API UBaseAbilitySystemComponent : public UAbilitySystemComponent
{
	GENERATED_BODY()

public:
	//能力演员的信息设置初始化
	void AbilityActorInfoSet();
	//资产标签委托
	FEffectAssetTags EffectAssetTags;
	//能力赋予委托,在能力被赋予时广播这个委托,用于给技能栏装备技能
	FAbilitesGiven AbilitiesGivenDelegate;
	//能力状态变更多播委托，用于更新升级后激活的新技能,发送能力标签和状态标签
	FAbilityStatusChanged AbilityStatusChangedDelegate;
	//技能装备委托，用于技能装备到技能栏
	FAbilityEquipped AbilityEquippedDelegate;
	//用于停止被动技能 委托
	FDeactivatePassiveAbility DeactivatePassiveAbilityDelegate;
	//被动能力粒子组件激活效果
	FActivatePassiveEffect ActivatePassiveEffectDelegate;
	
	//判断能力是否被赋予
	bool bStartupAbilitiesGiven = false;
	
	//添加角色能力 这里是用于角色部分启动角色能力调用的
	void AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities);
	//添加角色 被动启动能力
	void AddCharacterPassiveAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupPassiveAbilities);
	//从存档中读取并添加角色能力
	void AddCharacterAbilitiesFromSaveData(ULoadScreenSaveGame* SaveData);
	//能力按键按下函数
	void AbilityInputTagPressed(const FGameplayTag& InputTag);
	//能力按键松开函数
	void AbilityInputTagReleased(const FGameplayTag& InputTag);
	//能力按键按住函数
	void AbilityInputTagHeld(const FGameplayTag& InputTag);
	//遍历所有已激活的能力
	void ForEachAbility(const FForEachAbility Delegate);
	//获取游戏能力规格包含的能力标签
	static FGameplayTag GetAbilityTagFromSpec(const FGameplayAbilitySpec AbilitySpec);
	//获取游戏能力规格包含的输入按键标签
	static FGameplayTag GetInputTagFromSpec(const FGameplayAbilitySpec AbilitySpec);
	//获取能力状态标签
	static FGameplayTag GetStatusFromSpec(const FGameplayAbilitySpec& AbilitySpec);
	//获取能力状态标签,这是一个动态的 通过能力标签获取能力状态的函数 和 上面的有一些不同,这个函数内会通过能力标签调用上面的 GetStatusFromSpec 返回状态标签
	FGameplayTag GetStatusTagFromAbilityTag(const FGameplayTag& AbilityTag);
	//获取输入槽位标签，这是一个动态的 通过能力标签获取输入槽位标签的函数
	FGameplayTag GetSlotFromAbilityTag(const FGameplayTag& AbilityTag);
	//返回插槽是否为空
	bool SlotIsEmpty(const FGameplayTag& Slot);
	//能力规格中调用 槽位检查返回结果
	static bool AbilityHasSlot(const FGameplayAbilitySpec& Spec, const FGameplayTag& Slot);
	//判断能力是否拥有任何一个槽位
	static bool AbilityHasAnySlot(const FGameplayAbilitySpec& Spec);
	//判断是否是被动能力
	bool IsPassiveAbility(const FGameplayAbilitySpec& Spec) const;
	//为能力分配槽位
	static void AssignSlotToAbility(FGameplayAbilitySpec& Spec, const FGameplayTag& Slot);
	
	//获取该插槽能力规格
	FGameplayAbilitySpec* GetSpecWithSlot(const FGameplayTag& Slot);
	//获取对应技能能力标签的游戏能力规格
	FGameplayAbilitySpec* GetSpecFromAbilityTag(const FGameplayTag& AbilityTag);
	//升级属性函数
	void UpgradeAttribute(const FGameplayTag& AttributeTag);
	//服务器执行升级属性
	UFUNCTION(Server, Reliable)
	void ServerUpgradeAttribute(const FGameplayTag& AttributeTag);
	//根据等级查询并更新能力信息里是否有复合当前等级解锁的技能
	void UpdateAbilityStatuses(int32 Level);
	//服务器执行的升级技能处理，进行RPC 复制给客户端
	UFUNCTION(Server, Reliable)
	void ServerSpendSpellPoint(const FGameplayTag& AbilityTag);
	
	/** 这部分是技能插槽装备和清空 start **/
	//服务器处理技能（拖拽或指定）分配装备到对应的技能栏插槽
	UFUNCTION(Server, Reliable)
	void ServerEquipAbility(const FGameplayTag& AbilityTag, const FGameplayTag& Slot);
	//广播给客户端装备技能, 能力标签、状态标签、按键输入插槽标签、上一个输入插槽标签
	UFUNCTION(Client, Reliable)
	void ClientEquipAbility(const FGameplayTag& AbilityTag, const FGameplayTag& Status, const FGameplayTag& Slot, const FGameplayTag& PreviousSlot);
	//清空技能插槽，用于装备技能或脱出技能时 清空上一个或当前技能插槽
	static void ClearSlot(FGameplayAbilitySpec* Spec);
	//清空掉所有已激活能力中对应的插槽标签
	void ClearAbilitiesOfSlot(const FGameplayTag& Slot);
	//判断是否有该槽位标签
	static bool AbilityHasSlot(FGameplayAbilitySpec* Spec, const FGameplayTag& Slot);
	/** 这部分是技能插槽装备和清空 end **/
	
	//通过能力标签 获取能力描述
	bool GetDescriptionsByAbilityTag(const FGameplayTag& AbilityTag, FString& OutDescription, FString& OutNextLevelDescription);

	//多播激活被动能力粒子效果函数，用于广播被动能力激活粒子效果
	//这是一个视觉效果 所以设置为 Unreliable 不可靠 网络占用优先级较低节省资源
	UFUNCTION(NetMulticast, Unreliable)
	void MulticastActivatePassiveEffect(const FGameplayTag& AbilityTag, bool bActivate);
	
protected:
	//重写激活能力函数，用于复制给客户端激活能力
	virtual void OnRep_ActivateAbilities() override;
	//客户端游戏效果应用函数
	//FUNCTION中配置的是 客户端和可复制 这样 客户端就可以复制到服务器了
	UFUNCTION(Client, Reliable)
	void ClientEffectApplied(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle);
	//客户端更新技能能力状态
	UFUNCTION(Client, Reliable)
	void ClientUpdateAbilityStatus(const FGameplayTag& AttributeTag,const FGameplayTag& InputTag, const FGameplayTag& StatusTag, int32 AbilityLevel);
	
};
